
(function (window) {

KC.extend({
	START_TO_START : 0,
	START_TO_END : 1,
	END_TO_END : 2,
	END_TO_START : 3
});

KC.extend('range', function(doc) {
	this.startContainer = doc;
	this.startOffset = 0;
	this.endContainer = doc;
	this.endOffset = 0;
	this.collapsed = true;
	this.commonAncestorContainer = doc;
	function updateCollapsed() {
		this.collapsed = (this.startContainer === this.endContainer && this.startOffset === this.endOffset);
	}
	function updateCommonAncestor() {
		function scan(node, func) {
			if (node === doc) return;
			while (node) {
				if (func(node)) return;
				node = node.parentNode;
			}
		}
		var nodes = [];
		scan(this.startContainer, function(node) {
			nodes.push(node);
		});
		var ancestor = doc;
		scan(this.endContainer, function(node) {
			if (KC.inArray(node, nodes)) {
				ancestor = node;
				return true;
			}
		});
		this.commonAncestorContainer = ancestor;
	}
	function compareAndUpdate() {
		var rangeA = new KC.range(doc);
		var rangeB = new KC.range(doc);
		rangeA.startContainer = rangeA.endContainer = this.startContainer;
		rangeA.startOffset = rangeA.endOffset = this.startOffset;
		rangeB.startContainer = rangeB.endContainer = this.endContainer;
		rangeB.startOffset = rangeB.endOffset = this.endOffset;
		if (rangeA.compareBoundaryPoints(KC.START_TO_START, rangeB) == 1) {
			this.startContainer = this.endContainer;
			this.startOffset = this.endOffset;
		}
	}
	this.setStart = function(node, offset) {
		this.startContainer = node;
		this.startOffset = offset;
		if (this.endContainer === doc) {
			this.endContainer = node;
			this.endOffset = offset;
		}
		compareAndUpdate.call(this);
		updateCollapsed.call(this);
		updateCommonAncestor.call(this);
	};
	this.setEnd = function(node, offset) {
		this.endContainer = node;
		this.endOffset = offset;
		if (this.startContainer === doc) {
			this.startContainer = node;
			this.startOffset = offset;
		}
		compareAndUpdate.call(this);
		updateCollapsed.call(this);
		updateCommonAncestor.call(this);
	};
	this.setStartBefore = function(node) {
		this.setStart(node.parentNode || doc, KC.index(node));
	};
	this.setStartAfter = function(node) {
		this.setStart(node.parentNode || doc, KC.index(node) + 1);
	};
	this.setEndBefore = function(node) {
		this.setEnd(node.parentNode || doc, KC.index(node));
	};
	this.setEndAfter = function(node) {
		this.setEnd(node.parentNode || doc, KC.index(node) + 1);
	};
	this.selectNode = function(node) {
		this.setStartBefore(node);
		this.setEndAfter(node);
	};
	this.selectNodeContents = function(node) {
		if (node.nodeType == 3 || KC.isSingle(node)) {
			this.selectNode(node);
		} else {
			if (!KC.browser.IE) KC.trimNode(node);
			if (node.firstChild) {
				this.setStartBefore(node.firstChild);
				this.setEndAfter(node.lastChild);
			} else {
				this.setStart(node, 0);
				this.setEnd(node, 0);
			}
		}
	};
	this.collapse = function(toStart) {
		if (toStart) this.setEnd(this.startContainer, this.startOffset);
		else this.setStart(this.endContainer, this.endOffset);
	};
	this.compareBoundaryPoints = function(how, range) {
		var rangeA = this.toNativeRange();
		var rangeB = range.toNativeRange();
		if (KC.browser.IE) {
			var arr = {};
			arr[KC.START_TO_START] = 'StartToStart';
			arr[KC.START_TO_END] = 'EndToStart';
			arr[KC.END_TO_END] = 'EndToEnd';
			arr[KC.END_TO_START] = 'StartToEnd';
			var cmp = rangeA.compareEndPoints(arr[how], rangeB);
			if (cmp !== 0) return cmp;
			var nodeA, nodeB;
			if (how === KC.START_TO_START || how === KC.END_TO_START) nodeA = this.startContainer;
			if (how === KC.START_TO_END || how === KC.END_TO_END) nodeA = this.endContainer;
			if (how === KC.START_TO_START || how === KC.START_TO_END) nodeB = range.startContainer;
			if (how === KC.END_TO_END || KC.END_TO_START) nodeB = range.endContainer;
			if (nodeA === nodeB) return 0;
			if (how === KC.START_TO_START || how === KC.END_TO_START) return KC.isAncestor(nodeA, nodeB) ? -1 : 1;
			if (how === KC.END_TO_END || how === KC.START_TO_END) return KC.isAncestor(nodeA, nodeB) ? 1 : -1;
		} else {
			return rangeA.compareBoundaryPoints(how, rangeB);
		}
	};
	this.cloneRange = function() {
		var range = new KC.range(doc);
		range.setStart(this.startContainer, this.startOffset);
		range.setEnd(this.endContainer, this.endOffset);
		return range;
	};
	this.toString = function() {
		var nativeRange = this.toNativeRange();
		return KC.nativeRangeText(nativeRange);
	};
	this.extractContents = function() {
		// TODO
	};
	this.cloneContents = function() {
		// TODO
	};
	this.toNativeRange = function() {
		var startContainer = this.startContainer;
		var startOffset = this.startOffset;
		var endContainer = this.endContainer;
		var endOffset = this.endOffset;
		return KC.createNativeRange(doc, startContainer, startOffset, endContainer, endOffset);
	};
});

function getBeforeLength(node) {
	var doc = node.ownerDocument;
	var len = 0;
	var sibling = node.previousSibling;
	while (sibling) {
		if (sibling.nodeType == 1) {
			if (KC.isSingle(sibling)) {
				len += 1;
			} else {
				var range = KC.createNativeRange(doc);
				range.moveToElementText(sibling);
				len += range.text.length;
			}
		} else if (sibling.nodeType == 3) {
			len += sibling.nodeValue.length;
		}
		sibling = sibling.previousSibling;
	}
	return len;
}

function getEndRange(node, offset) {
	var doc = node.ownerDocument || node;
	var range = KC.createNativeRange(doc);
	if (doc == node) {
		range.collapse(true);
		return range;
	}
	if (node.nodeType == 1) {
		var children = node.childNodes;
		var isStart, child, isTemp = false;
		if (offset == 0) {
			child = children[0];
			isStart = true;
		} else {
			child = children[offset - 1];
			isStart = false;
		}
		if (!child) {
			var temp = doc.createTextNode(' ');
			node.appendChild(temp);
			child = temp;
			isTemp = true;
		}
		if (child.nodeName.toLowerCase() === 'head') {
			if (offset === 1) isStart = true;
			if (offset === 2) isStart = false;
			range.collapse(isStart);
			return range;
		}
		if (child.nodeType == 1) {
			range.moveToElementText(child);
			range.collapse(isStart);
		} else {
			range.moveToElementText(node);
			if (isTemp) node.removeChild(temp);
			var len = getBeforeLength(child);
			len = isStart ? len : len + child.nodeValue.length;
			range.moveStart('character', len);
		}
	} else if (node.nodeType == 3) {
		range.moveToElementText(node.parentNode);
		range.moveStart('character', offset + getBeforeLength(node));
	}
	return range;
}

KC.extend({
	createRange : function(doc) {
		return new KC.range(doc);
	},
	toRange : function(nativeRange) {
		if (KC.browser.IE) {
			var doc = nativeRange.parentElement().ownerDocument;
			if (nativeRange.item) {
				var range = new KC.range(doc);
				range.selectNode(nativeRange.item(0));
				return range;
			}
			function getStartEnd(isStart) {
				var range = new KC.range(doc);
				var pointRange = nativeRange.duplicate();
				pointRange.collapse(isStart);
				var parent = pointRange.parentElement();
				var children = parent.childNodes;
				if (children.length == 0) {
					range.selectNode(parent);
					return {node: range.startContainer, offset: range.startOffset};
				}
				var startNode = doc, startPos = 0, isEnd = false;
				var testRange = nativeRange.duplicate();
				testRange.moveToElementText(parent);
				for (var i = 0, len = children.length; i < len; i++) {
					var node = children[i];
					var cmp = testRange.compareEndPoints('StartToStart', pointRange);
					if (cmp > 0) isEnd = true;
					if (cmp == 0) {
						var range = new KC.range(doc);
						if (node.nodeType == 1) range.selectNode(node);
						else range.setStartBefore(node);
						return {node: range.startContainer, offset: range.startOffset};
					}
					if (node.nodeType == 1) {
						var nodeRange = nativeRange.duplicate();
						nodeRange.moveToElementText(node);
						testRange.setEndPoint('StartToEnd', nodeRange);
						if (isEnd) startPos += nodeRange.text.length;
						else startPos = 0;
					} else if (node.nodeType == 3) {
						testRange.moveStart('character', node.nodeValue.length);
						startPos += node.nodeValue.length;
					}
					if (!isEnd) startNode = node;
				}
				if (!isEnd && startNode.nodeType == 1) {
					range.setStartAfter(parent.lastChild);
					return {node: range.startContainer, offset: range.startOffset};
				}
				testRange = nativeRange.duplicate();
				testRange.moveToElementText(parent);
				testRange.setEndPoint('StartToEnd', pointRange);
				startPos -= KC.nativeRangeText(testRange).length;
				return {node: startNode, offset: startPos};
			}
			var start = getStartEnd(true);
			var end = getStartEnd(false);
			var range = new KC.range(doc);
			range.setStart(start.node, start.offset);
			range.setEnd(end.node, end.offset);
			return range;
		} else {
			var doc = nativeRange.startContainer.ownerDocument;
			console.log(nativeRange);
			//KC.trimNode(nativeRange.startContainer);
			console.log(nativeRange);
			KC.trimNode(nativeRange.endContainer);
			console.log(nativeRange);
			var range = new KC.range(doc);
			range.setStart(nativeRange.startContainer, nativeRange.startOffset);
			range.setEnd(nativeRange.endContainer, nativeRange.endOffset);
			return range;
		}
	},
	createNativeRange : function(doc, startContainer, startOffset, endContainer, endOffset) {
		var range;
		if (doc.createRange) {
			range = doc.createRange();
			range.selectNodeContents(doc.body);
		} else {
			range = doc.body.createTextRange();
		}
		if (arguments.length == 1) {
			return range;
		} else {
			if (KC.browser.IE) {
				range.setEndPoint('StartToStart', getEndRange(startContainer, startOffset));
				range.setEndPoint('EndToStart', getEndRange(endContainer, endOffset));
			} else {
				range.setStart(startContainer, startOffset);
				range.setEnd(endContainer, endOffset);
			}
			return range;
		}
	},
	nativeRangeText : function(nativeRange) {
		var str = 'text' in nativeRange ? nativeRange.text : nativeRange.toString();
		return str.replace(/\r\n|\n|\r/g, '');
	}
});

})(window);
